package com.myd.threadsafe;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 演示线程的原子性问题
 * 两个线程读取一个数值变量
 * @Author:
 * @DateTime: 2023/4/8 22:57
 * @Description:
 */
public class Test01 {
    public static void main(String[] args) {

        //启动两个线程，不断调用getNum() 方法
        MyInt myInt = new MyInt();

        //通过for循环创建并启动两个线程
        for (int i = 1; i <=2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) { //不断调用getNum() 方法
                        System.out.println(Thread.currentThread().getName() + " -> " + myInt.getNum());
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();

        }

    }

    //错误
    //该方法被多个线程调用后会出现多个线程中打印出相同的数值，如：相同的0、相同的1（可自行调用看看结果）
    //这里写的是一个静态内部类，不这样写也行，在外部写类也行
    static class MyInt1 {
        int num;

        public int getNum() {
            return num++;
            /*
                加加操作是先读、再加、再赋值
                ++自增操作实现的步骤：
                    1、读取num的值
                    2、num自增
                    3、把自增后的值再赋值给num变量


                这样会出现问题，如：
                    当Thread-0调用getNum()方法做完1、2步后，Thread-1也调用了getNum()方法，此时num仍为0，
                    这样就会出现Thread-0和Thread-1调用getNum()方法后，都输出结果0的问题

                我们应该把1、2、3三步看作是一个原子操作，即等Thread-0调用getNum()方法做完1、2、3步后，
                Thread-1再调用getNum()方法做1、2、3步
             */
        }
    }

    //正确
    //该方法被多个线程调用后不会出现多个线程中打印出相同的数值，而是依次递增
    //在java中提供了一个线程安全的AtomicInteger类，保证了操作的原子性
    static class MyInt {
        AtomicInteger num = new AtomicInteger();

        public int getNum() {
            return num.getAndIncrement();
        }
    }

}
